package com.framework.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.framework.core.common.bo.SysUserBO;
import com.framework.core.common.constant.MenuConstants;
import com.framework.core.common.enums.*;
import com.framework.core.exception.CustomException;
import com.framework.core.util.SecurityUtil;
import com.framework.system.entity.SysMenu;
import com.framework.system.entity.SysRoleMenu;
import com.framework.system.entity.SysUserRole;
import com.framework.system.entity.dto.SysMenuAddDTO;
import com.framework.system.entity.dto.SysMenuQueryDTO;
import com.framework.system.entity.dto.SysMenuUpdateDTO;
import com.framework.system.entity.vo.RouteItemVO;
import com.framework.system.entity.vo.RouteMetaVO;
import com.framework.system.entity.vo.SysMenuListVO;
import com.framework.system.entity.vo.SysMenuVO;
import com.framework.system.mapper.SysMenuMapper;
import com.framework.system.mapper.SysRoleMenuMapper;
import com.framework.system.mapper.SysUserRoleMapper;
import com.framework.system.service.ISysMenuService;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单表  服务实现类
 * </p>
 *
 * @author Kevin
 * @since 2021-06-19
 */
@Service
@AllArgsConstructor
public class SysMenuServiceImpl implements ISysMenuService {

    private SysMenuMapper sysMenuMapper;

    private SysUserRoleMapper sysUserRoleMapper;

    private SysRoleMenuMapper sysRoleMenuMapper;

    @Override
    public Page<SysMenuListVO> selectSysMenuWithPage(Page reqPage, SysMenuQueryDTO req) {
        LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(Objects.nonNull(req.getParentId()), SysMenu::getParentId, req.getParentId());
        queryWrapper.like(StrUtil.isNotBlank(req.getMenuName()), SysMenu::getMenuName, req.getMenuName());
        queryWrapper.orderByAsc(SysMenu::getSort);
        Page<SysMenu> page = sysMenuMapper.selectPage(reqPage, queryWrapper);
        Page<SysMenuListVO> voPage = BeanUtil.copyProperties(page, Page.class);
        voPage.setRecords(BeanUtil.copyToList(page.getRecords(), SysMenuListVO.class));
        return voPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSysMenu(SysMenuAddDTO req) {
        // 当前节点为目录
        if (Objects.nonNull(req.getParentId()) && Objects.equals(req.getMenuType(), MenuTypeEnum.DIR.getCode())) {
            SysMenu sysMenuParent = sysMenuMapper.selectById(req.getParentId());
            if (Objects.equals(sysMenuParent.getMenuType(), MenuTypeEnum.MENU.getCode())) {
                throw new CustomException("上级节点不允许为菜单");
            }
            if (Objects.equals(sysMenuParent.getMenuType(), MenuTypeEnum.BUTTON.getCode())) {
                throw new CustomException("上级节点不允许为按钮");
            }
        }
        // 当前节点为菜单
        if (Objects.nonNull(req.getParentId()) && Objects.equals(req.getMenuType(), MenuTypeEnum.MENU.getCode())) {
            SysMenu sysMenuParent = sysMenuMapper.selectById(req.getParentId());
            if (Objects.equals(sysMenuParent.getMenuType(), MenuTypeEnum.MENU.getCode())) {
                throw new CustomException("上级节点不允许为菜单");
            }
            if (Objects.equals(sysMenuParent.getMenuType(), MenuTypeEnum.BUTTON.getCode())) {
                throw new CustomException("上级节点不允许为按钮");
            }
        }
        SysMenu entity = BeanUtil.copyProperties(req, SysMenu.class);
        if (Objects.isNull(req.getParentId())) {
            entity.setParentId(0L);
        }
        sysMenuMapper.insert(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSysMenu(SysMenuUpdateDTO req) {
        // 当前节点为目录
        if (Objects.nonNull(req.getParentId()) && Objects.equals(req.getMenuType(), MenuTypeEnum.DIR.getCode())) {
            SysMenu sysMenuParent = sysMenuMapper.selectById(req.getParentId());
            if (Objects.equals(sysMenuParent.getMenuType(), MenuTypeEnum.MENU.getCode())) {
                throw new CustomException("上级节点不允许为菜单");
            }
            if (Objects.equals(sysMenuParent.getMenuType(), MenuTypeEnum.BUTTON.getCode())) {
                throw new CustomException("上级节点不允许为按钮");
            }
        }
        // 当前节点为菜单
        if (Objects.nonNull(req.getParentId()) && Objects.equals(req.getMenuType(), MenuTypeEnum.MENU.getCode())) {
            SysMenu sysMenuParent = sysMenuMapper.selectById(req.getParentId());
            if (Objects.equals(sysMenuParent.getMenuType(), MenuTypeEnum.MENU.getCode())) {
                throw new CustomException("上级节点不允许为菜单");
            }
            if (Objects.equals(sysMenuParent.getMenuType(), MenuTypeEnum.BUTTON.getCode())) {
                throw new CustomException("上级节点不允许为按钮");
            }
        }
        SysMenu entity = BeanUtil.copyProperties(req, SysMenu.class);
        if (Objects.isNull(req.getParentId())) {
            entity.setParentId(0L);
        }
        if (entity.getMenuType().equals(MenuTypeEnum.DIR.getCode())) {
            entity.setComponent("");
            entity.setPermission("");
            entity.setKeepalive("");
            entity.setLinkExternal("");
            entity.setFrame("");
            entity.setLinkUrl("");
        }
        if (entity.getMenuType().equals(MenuTypeEnum.MENU.getCode())) {
            entity.setPermission("");
            if (entity.getLinkExternal().equals(LinkExternalEnum.NO.getCode())) {
                entity.setFrame("");
                entity.setLinkUrl("");
            }
        }
        if (entity.getMenuType().equals(MenuTypeEnum.BUTTON.getCode())) {
            entity.setIcon("");
            entity.setRoutePath("");
            entity.setComponent("");
            entity.setVisible("");
            entity.setKeepalive("");
            entity.setLinkExternal("");
            entity.setFrame("");
            entity.setLinkUrl("");
        }
        sysMenuMapper.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeSysMenu(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));
        idList.forEach(item -> {
            SysMenu sysMenu = sysMenuMapper.selectById(item);
            SysMenu sysMenuChild = sysMenuMapper.selectOne(Wrappers.lambdaQuery(SysMenu.class).eq(SysMenu::getParentId, item));
            if (Objects.nonNull(sysMenuChild)) {
                throw new CustomException(String.format("菜单【%s】存在下级菜单，无法删除", sysMenu.getMenuName()));
            }
        });
        idList.forEach(item -> {
            sysMenuMapper.deleteById(item);
        });
    }

    @Override
    public SysMenuVO getSysMenu(String id) {
        SysMenu entity = sysMenuMapper.selectById(id);
        return BeanUtil.copyProperties(entity, SysMenuVO.class);
    }

    @Override
    public List<Tree<String>> selectSysMenuAndExcludeNodeWithOptionTree(String nodeId) {
        List<SysMenu> sysMenuList = sysMenuMapper.selectList(Wrappers.lambdaQuery(SysMenu.class).ne(StrUtil.isNotBlank(nodeId), SysMenu::getId, nodeId));
        List treeNodeList = sysMenuList.stream().map(item -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setId(item.getId().toString());
            treeNode.setName(item.getMenuName());
            treeNode.setParentId(item.getParentId().toString());
            treeNode.setWeight(item.getSort());
            Map extra = Maps.newHashMap();
            extra.put("icon", item.getIcon());
            treeNode.setExtra(extra);
            return treeNode;
        }).collect(Collectors.toList());
        List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "0");
        return CollectionUtils.isEmpty(treeList) ? new ArrayList() : treeList;
    }

    @Override
    public List<Tree<String>> selectSysMenuWithTree() {
        List<SysMenu> sysMenuList = sysMenuMapper.selectList(Wrappers.emptyWrapper());
        List treeNodeList = sysMenuList.stream().map(item -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setId(item.getId().toString());
            treeNode.setName(item.getMenuName());
            treeNode.setParentId(item.getParentId().toString());
            treeNode.setWeight(item.getSort());
            Map extra = Maps.newHashMap();
            extra.put("icon", item.getIcon());
            treeNode.setExtra(extra);
            return treeNode;
        }).collect(Collectors.toList());
        List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "0");
        return CollectionUtils.isEmpty(treeList) ? new ArrayList() : treeList;
    }

    @Override
    public Set<String> getPermissionsByUserId(Long userId) {
        // 查询角色列表
        List<SysUserRole> sysUserRoleList = sysUserRoleMapper.selectList(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, userId));
        if (sysUserRoleList.isEmpty()) {
            return Sets.newHashSet();
        }
        List<Long> roleIdList = sysUserRoleList.stream().map(item -> item.getRoleId()).collect(Collectors.toList());
        // 查询按钮菜单权限标识
        List<SysRoleMenu> sysRoleMenuList = sysRoleMenuMapper.selectList(Wrappers.lambdaQuery(SysRoleMenu.class).in(SysRoleMenu::getRoleId, roleIdList));
        if (sysRoleMenuList.isEmpty()) {
            return Sets.newHashSet();
        }
        // 查询菜单
        Set<Long> menuIds = sysRoleMenuList.stream().map(item -> item.getMenuId()).collect(Collectors.toSet());
        LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(SysMenu::getMenuType, MenuTypeEnum.BUTTON.getCode());
        queryWrapper.eq(SysMenu::getStatus, StatusEnum.YES.getCode());
        queryWrapper.in(SysMenu::getId, menuIds);
        List<SysMenu> sysMenuList = sysMenuMapper.selectList(queryWrapper);
        return sysMenuList.stream().map(item -> item.getPermission()).collect(Collectors.toSet());
    }

    @Override
    public List<RouteItemVO> selectGrantMenuWithList() {
        List<SysMenu> sysMenuList;
        SysUserBO sysUserBO = SecurityUtil.getLoginUser().getSysUser();
        if (SecurityUtil.isRootGroup()) {
            LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(SysMenu::getStatus, StatusEnum.YES.getCode());
            queryWrapper.in(SysMenu::getMenuType, MenuTypeEnum.DIR.getCode(), MenuTypeEnum.MENU.getCode());
            sysMenuList = sysMenuMapper.selectList(queryWrapper);
        } else {
            sysMenuList = this.listGrantMenuByUserId(sysUserBO.getId());
        }
        List<RouteItemVO> routeItemVOList = sysMenuList.stream()
                .filter(item -> item.getParentId().intValue() == 0)
                .sorted(Comparator.comparing(SysMenu::getSort))
                .map(item -> {
                    RouteItemVO node = convertToRoute(item);
                    node.setChildren(getChildrenList(item, sysMenuList));
                    return node;
                }).collect(Collectors.toList());
        return routeItemVOList;
    }

    private List<SysMenu> listGrantMenuByUserId(Long userId) {
        // 查询角色列表
        List<SysUserRole> sysUserRoleList = sysUserRoleMapper.selectList(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, userId));
        if (sysUserRoleList.isEmpty()) {
            return new ArrayList();
        }
        List<Long> roleIdList = sysUserRoleList.stream().map(item -> item.getRoleId()).collect(Collectors.toList());
        // 查询角色菜单
        List<SysRoleMenu> sysRoleMenuList = sysRoleMenuMapper.selectList(Wrappers.lambdaQuery(SysRoleMenu.class).in(SysRoleMenu::getRoleId, roleIdList));
        if (sysRoleMenuList.isEmpty()) {
            return new ArrayList();
        }
        // 查询菜单
        Set<Long> menuIds = sysRoleMenuList.stream().map(item -> item.getMenuId()).collect(Collectors.toSet());
        LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(SysMenu::getStatus, StatusEnum.YES.getCode());
        queryWrapper.in(SysMenu::getMenuType, MenuTypeEnum.DIR.getCode(), MenuTypeEnum.MENU.getCode());
        queryWrapper.in(SysMenu::getId, menuIds);
        return sysMenuMapper.selectList(queryWrapper);
    }

    private List<RouteItemVO> getChildrenList(SysMenu root, List<SysMenu> list) {
        List<RouteItemVO> childrenList = list.stream()
                .filter(item -> item.getParentId().equals(root.getId()))
                .sorted(Comparator.comparing(SysMenu::getSort))
                .map(item -> {
                    RouteItemVO node = convertToRoute(item);
                    node.setChildren(getChildrenList(item, list));
                    return node;
                }).collect(Collectors.toList());
        return childrenList;
    }

    private RouteItemVO convertToRoute(SysMenu item) {
        RouteItemVO node = new RouteItemVO();
        node.setName(StrUtil.upperFirst(item.getRoutePath()));
        node.setPath(item.getRoutePath());
        node.setComponent(item.getComponent());
        // 一级目录
        if (Objects.equals(item.getMenuType(), MenuTypeEnum.DIR.getCode()) && item.getParentId().intValue() == 0) {
            node.setPath("/" + item.getRoutePath());
            node.setComponent(MenuConstants.LAYOUT);
        }
        // 外部链接
        if (Objects.equals(item.getMenuType(), MenuTypeEnum.MENU.getCode()) && Objects.equals(item.getLinkExternal(), LinkExternalEnum.YES.getCode())) {
            node.setComponent(MenuConstants.IFRAME);
        }
        RouteMetaVO routeMetaVO = new RouteMetaVO();
        routeMetaVO.setTitle(item.getMenuName());
        routeMetaVO.setIcon(item.getIcon());
        routeMetaVO.setHideMenu(StrUtil.isNotBlank(item.getVisible()) && item.getVisible().equals(VisibleEnum.HIDE.getCode()));
        // 菜单
        if (Objects.equals(item.getMenuType(), MenuTypeEnum.MENU.getCode())) {
            routeMetaVO.setIgnoreKeepAlive(item.getKeepalive().equals(KeepaliveEnum.YES.getCode()));
        }
        // 外部链接
        if (Objects.equals(item.getMenuType(), MenuTypeEnum.MENU.getCode()) && Objects.equals(item.getLinkExternal(), LinkExternalEnum.YES.getCode())) {
            // 内嵌
            if (Objects.equals(item.getFrame(), FrameEnum.YES.getCode())) {
                routeMetaVO.setFrameSrc(item.getLinkUrl());
            }
            // 外嵌
            if (item.getFrame().equals(FrameEnum.NO.getCode())) {
                node.setPath(item.getLinkUrl());
            }
        }
        node.setMeta(routeMetaVO);
        return node;
    }

}
